/**
 * Created by cheesu on 2015/8/17.
 */

/**
 * React文件上传组件，兼容IE8+
 * 现代浏览器采用AJAX（XHR2+File API）上传。低版本浏览器使用form+iframe上传。
 * 使用到ES6，需要经babel转译
 */

/*eslint indent: 0 */
import React from 'react'
import PT from 'prop-types'
const emptyFunction = function() {}
/*当前IE上传组的id*/
let currentIEID = 0
/*存放当前IE上传组的可用情况*/
const IEFormGroup = [true]
/*当前xhr的数组（仅有已开始上传之后的xhr）*/
let xhrList = []
let currentXHRID = 0

class FileUpload extends React.Component{
    constructor(props){
        super(props)
        this.state = {
            chooseBtn: {},       //选择按钮。如果chooseAndUpload=true代表选择并上传。
            uploadBtn: {},       //上传按钮。如果chooseAndUpload=true则无效。
            before: [],      //存放props.children中位于chooseBtn前的元素
            middle: [],      //存放props.children中位于chooseBtn后，uploadBtn前的元素
            after: []        //存放props.children中位于uploadBtn后的元素,
        }
    }

    /*根据props更新组件*/
    _updateProps(props) {
        this.isIE = !(this.checkIE() < 0 || this.checkIE() >= 10)
        const options = props.options
        this.baseUrl = options.baseUrl     //域名
        this.param = options.param     //get参数
        this.chooseAndUpload = options.chooseAndUpload || false      //是否在用户选择了文件之后立刻上传
        this.paramAddToField = options.paramAddToField || undefined  //需要添加到FormData的对象。不支持IE
        /*upload success 返回resp的格式*/
        this.dataType = 'json'
        options.dataType && (options.dataType.toLowerCase() == 'text') && (this.dataType = 'text')
        this.wrapperDisplay = options.wrapperDisplay || 'inline-block'     //包裹chooseBtn或uploadBtn的div的display
        this.timeout = (typeof options.timeout == 'number' && options.timeout > 0) ? options.timeout : 0     //超时时间
        this.accept = options.accept || ''  //限制文件后缀
        this.multiple = options.multiple || false
        this.numberLimit = options.numberLimit || false //允许多文件上传时，选择文件数量的限制
        this.fileFieldName = options.fileFieldName || false //文件附加到formData上时的key，传入string指定一个file的属性名，值为其属性的值。不支持IE
        this.withCredentials = options.withCredentials || false //跨域时是否使用认证信息
        this.requestHeaders = options.requestHeaders || false //要设置的请求头键值对

        /*生命周期函数*/
        /**
         * beforeChoose() : 用户选择之前执行，返回true继续，false阻止用户选择
         * @param  null
         * @return  {boolean} 是否允许用户进行选择
         */
        this.beforeChoose = options.beforeChoose || emptyFunction
        /**
         * chooseFile(file) : 用户选择文件后的触发的回调函数
         * @param file {File | string} 现代浏览器返回File对象，IE返回文件名
         * @return
         */
        this.chooseFile = options.chooseFile || emptyFunction
        /**
         * beforeUpload(file,mill) : 用户上传之前执行，返回true继续，false阻止用户选择
         * @param file {File | string} 现代浏览器返回File对象，IE返回文件名
         * @param mill {long} 毫秒数，如果File对象已有毫秒数则返回一样的
         * @return  {boolean || object} 是否允许用户进行上传 (hack:如果是obj{
         *     assign:boolean 默认true
         *     param:object
         * }), 则对本次的param进行处理
         */
        this.beforeUpload = options.beforeUpload || emptyFunction
        /**
         * doUpload(file,mill) : 上传动作(xhr send | form submit)执行后调用
         * @param file {File | string} 现代浏览器返回File对象，IE返回文件名
         * @param mill {long} 毫秒数，如果File对象已有毫秒数则返回一样的
         * @return
         */
        this.doUpload = options.doUpload || emptyFunction
        /**
         * uploading(progress) : 在文件上传中的时候，浏览器会不断触发此函数。IE中使用每200ms触发的假进度
         * @param progress {Progress} progress对象，里面存有例如上传进度loaded和文件大小total等属性
         * @return
         */
        this.uploading = options.uploading || emptyFunction
        /**
         * uploadSuccess(resp) : 上传成功后执行的回调（针对AJAX而言）
         * @param resp {json | string} 根据options.dataType指定返回数据的格式
         * @return
         */
        this.uploadSuccess = options.uploadSuccess || emptyFunction
        /**
         * uploadError(err) : 上传错误后执行的回调（针对AJAX而言）
         * @param err {Error | object} 如果返回catch到的error，其具有type和message属性
         * @return
         */
        this.uploadError = options.uploadError || emptyFunction
        /**
         * uploadFail(resp) : 上传失败后执行的回调（针对AJAX而言）
         * @param resp {string} 失败信息
         */
        this.uploadFail = options.uploadFail || emptyFunction
        /**
         * onabort(mill, xhrID) : 主动取消xhr进程的响应
         * @param mill {long} 毫秒数，本次上传时刻的时间
         * @param xhrID {int} 在doUpload时会返回的当次xhr代表ID
         */
        this.onabort = options.onabort || emptyFunction

        this.files = options.files || this.files || false        //保存需要上传的文件
        /*特殊内容*/

        /*IE情况下，由于上传按钮被隐藏的input覆盖，不能进行disabled按钮处理。
         * 所以当disabledIEChoose为true（或者func返回值为true）时，禁止IE上传。
         */
        this.disabledIEChoose = options.disabledIEChoose || false

        this._withoutFileUpload = options._withoutFileUpload || false      //不带文件上传，为了给秒传功能使用，不影响IE
        this.filesToUpload = options.filesToUpload || []       //使用filesToUpload()方法代替
        this.textBeforeFiles = options.textBeforeFiles || false //make this true to add text fields before file data
        /*使用filesToUpload()方法代替*/
        if (this.filesToUpload.length && !this.isIE) {
            this.filesToUpload.forEach( file => {
                this.files = [file]
                this.commonUpload()
            })
        }

        /*放置虚拟DOM*/
        let chooseBtn, uploadBtn, flag = 0
        const before = [], middle = [], after = []
        if (this.chooseAndUpload) {
            React.Children.forEach(props.children, (child)=> {
                if (child && child.ref == 'chooseAndUpload') {
                    chooseBtn = child
                    flag++
                } else {
                    flag == 0 ? before.push(child) : flag == 1 ? middle.push(child) : ''
                }
            })
        } else {
            React.Children.forEach(props.children, (child)=> {
                if (child && child.ref == 'chooseBtn') {
                    chooseBtn = child
                    flag++
                } else if (child && child.ref == 'uploadBtn') {
                    uploadBtn = child
                    flag++
                } else {
                    flag == 0 ? before.push(child) : flag == 1 ? middle.push(child) : after.push(child)
                }
            })
        }
        this.setState({
            chooseBtn,
            uploadBtn,
            before,
            middle,
            after
        })
    }

    /*触发隐藏的input框选择*/
    /*触发beforeChoose*/
    commonChooseFile() {
        const jud = this.beforeChoose()
        if (jud != true && jud != undefined) return
        this.refs['ajax_upload_file_input'].click()
    }
    /*现代浏览器input change事件。File API保存文件*/
    /*触发chooseFile*/
    commonChange(e) {
        let files
        e.dataTransfer ? files = e.dataTransfer.files :
          e.target ? files = e.target.files : ''

        /*如果限制了多文件上传时的数量*/
        const numberLimit = typeof this.numberLimit === 'function' ? this.numberLimit() : this.numberLimit
        if(this.multiple && numberLimit && files.length > numberLimit) {
            const newFiles = {}
            for(let i = 0; i< numberLimit; i++) newFiles[i] = files[i]
            newFiles.length = numberLimit
            files = newFiles
        }
        this.files = files
        this.chooseFile(files)
        this.chooseAndUpload && this.commonUpload()
    }
    
    /*执行上传*/
    commonUpload() {
        /*mill参数是当前时刻毫秒数，file第一次进行上传时会添加为file的属性，也可在beforeUpload为其添加，之后同一文件的mill不会更改，作为文件的识别id*/
        const mill = (this.files.length && this.files[0].mill) || (new Date).getTime()
        const jud = this.beforeUpload(this.files, mill)
        if (jud != true && jud != undefined && typeof jud != 'object') {
            /*清除input的值*/
            this.refs['ajax_upload_file_input'].value = ''
            return
        }



        if (!this.files) return
        if (!this.baseUrl) throw new Error('baseUrl missing in options')

        /*用于存放当前作用域的东西*/
        const scope = {}
        /*组装FormData*/
        let formData = new FormData()
        /*If we need to add fields before file data append here*/
        if(this.textBeforeFiles){
           formData = this.appendFieldsToFormData(formData);
        }
        if (!this._withoutFileUpload) {
            const fieldNameType = typeof this.fileFieldName

            /*判断是用什么方式作为formdata item 的 name*/
            Object.keys(this.files).forEach(key => {
                if(key == 'length') return

                if(fieldNameType == 'function') {
                    const file = this.files[key]
                    const fileFieldName = this.fileFieldName(file)
                    formData.append(fileFieldName, file)
                }else if(fieldNameType == 'string') {
                    const file = this.files[key]
                    formData.append(this.fileFieldName, file)
                }else {
                    const file = this.files[key]
                    formData.append(file.name, file)
                }
            })

        }
        /*If we need to add fields after file data append here*/
        if(!this.textBeforeFiles){
          formData = this.appendFieldsToFormData(formData);
        }
        const baseUrl = this.baseUrl

        /*url参数*/
        /*如果param是一个函数*/
        const param = typeof this.param === 'function' ? this.param(this.files) : this.param

        let paramStr = ''

        if (param) {
            const paramArr = []
            param['_'] = mill
            Object.keys(param).forEach(key =>
              paramArr.push(`${key}=${param[key]}`)
            )
            paramStr = '?' + paramArr.join('&')
        }
        const targeturl = baseUrl + paramStr

        /*AJAX上传部分*/
        const xhr = new XMLHttpRequest()
        xhr.open('POST', targeturl, true)

        /*跨域是否开启验证信息*/
        xhr.withCredentials = this.withCredentials
        /*是否需要设置请求头*/
        const rh = this.requestHeaders
        rh && Object.keys(rh).forEach(key =>
            xhr.setRequestHeader(key, rh[key])
        )

        /*处理超时。用定时器判断超时，不然xhr state=4 catch的错误无法判断是超时*/
        if(this.timeout) {
            xhr.timeout = this.timeout
            xhr.ontimeout = () => {
                this.uploadError({type: 'TIMEOUTERROR', message: 'timeout'})
                scope.isTimeout = false
            }
            scope.isTimeout = false
            setTimeout(()=>{
                scope.isTimeout = true
            },this.timeout)
        }

        xhr.onreadystatechange = () => {
            /*xhr finish*/
            try {
                if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 400) {
                    const resp = this.dataType == 'json' ? JSON.parse(xhr.responseText) : xhr.responseText
                    this.uploadSuccess(resp)
                } else if (xhr.readyState == 4) {
                    /*xhr fail*/
                    const resp = this.dataType == 'json' ? JSON.parse(xhr.responseText) : xhr.responseText
                    this.uploadFail(resp)
                }
            } catch (e) {
                /*超时抛出不一样的错误，不在这里处理*/
                !scope.isTimeout && this.uploadError({type: 'FINISHERROR', message: e.message})
            }
        }
        /*xhr error*/
        xhr.onerror = () => {
            try {
                const resp = this.dataType == 'json' ? JSON.parse(xhr.responseText) : xhr.responseText
                this.uploadError({type: 'XHRERROR', message: resp})
            } catch (e) {
                this.uploadError({type: 'XHRERROR', message: e.message})
            }
        }
        /*这里部分浏览器实现不一致，而且IE没有这个方法*/
        xhr.onprogress = xhr.upload.onprogress = progress => {
            this.uploading(progress, mill)
        }

        /*不带文件上传，给秒传使用*/
        this._withoutFileUpload ? xhr.send(null) : xhr.send(formData)

        /*保存xhr id*/
        xhrList.push(xhr)
        const cID = xhrList.length - 1
        currentXHRID = cID

        /*有响应abort的情况*/
        xhr.onabort = () => this.onabort(mill, cID)

        /*trigger执行上传的用户回调*/
        this.doUpload(this.files, mill, currentXHRID)

        /*清除input的值*/
        this.refs['ajax_upload_file_input'].value = ''
    }

    /*组装自定义添加到FormData的对象*/
    appendFieldsToFormData(formData){
        const field = typeof this.paramAddToField == 'function' ? this.paramAddToField() : this.paramAddToField
        field &&
            Object.keys(field).map(index=>
                formData.append(index, field[index])
            )
        return formData
    }

    /*iE选择前验证*/
    /*触发beforeChoose*/
    IEBeforeChoose(e) {
        const jud = this.beforeChoose()
        jud != true && jud != undefined && e.preventDefault()
    }
    /*IE需要用户真实点击上传按钮，所以使用透明按钮*/
    /*触发chooseFile*/
    IEChooseFile(e) {
        this.fileName = e.target.value.substring(e.target.value.lastIndexOf('\\') + 1)
        this.chooseFile(this.fileName)
        /*先执行IEUpload，配置好action等参数，然后submit*/
        this.chooseAndUpload && (this.IEUpload() !== false) &&
            document.getElementById(`ajax_upload_file_form_${this.IETag}${currentIEID}`).submit()
        e.target.blur()
    }
    /*IE处理上传函数*/
    /*触发beforeUpload doUpload*/
    IEUpload(e) {
        const mill = (new Date).getTime()
        const jud = this.beforeUpload(this.fileName, mill)
        if(!this.fileName || (jud != true && jud != undefined) ) {
            e && e.preventDefault()
            return false
        }
        const that = this

        /*url参数*/
        const baseUrl = this.baseUrl

        const param = typeof this.param === 'function' ? this.param(this.fileName) : this.param
        let paramStr = ''

        if (param) {
            const paramArr = []
            param['_'] = mill
            param['ie'] === undefined && (param['ie'] = 'true')
            for (const key in param) {
                if(param[key] != undefined) paramArr.push(`${key}=${param[key]}`)
            }
            paramStr = '?' + paramArr.join('&')
        }
        const targeturl = baseUrl + paramStr

        document.getElementById(`ajax_upload_file_form_${this.IETag}${currentIEID}`).setAttribute('action', targeturl)
        /*IE假的上传进度*/
        const getFakeProgress = this.fakeProgress()
        let loaded = 0,
          count = 0

        const progressInterval = setInterval(() => {
            loaded = getFakeProgress(loaded)
            this.uploading({
                loaded,
                total: 100
            },mill)
            /*防止永久执行，设定最大的次数。暂时为30秒(200*150)*/
            ++count >= 150 && clearInterval(progressInterval)
        },200)


        /*当前上传id*/
        const partIEID = currentIEID
        /*回调函数*/
        window.attachEvent ?
          document.getElementById(`ajax_upload_file_frame_${this.IETag}${partIEID}`).attachEvent('onload', handleOnLoad) :
          document.getElementById(`ajax_upload_file_frame_${this.IETag}${partIEID}`).addEventListener('load', handleOnLoad)


        function handleOnLoad() {
            /*clear progress interval*/
            clearInterval(progressInterval)
            try {
                that.uploadSuccess(that.IECallback(that.dataType, partIEID))
            } catch (e) {
                that.uploadError(e)
            } finally {
                /*清除输入框的值*/
                const oInput = document.getElementById(`ajax_upload_hidden_input_${that.IETag}${partIEID}`)
                oInput.outerHTML = oInput.outerHTML
            }
        }
        this.doUpload(this.fileName, mill)
        /*置为非空闲*/
        IEFormGroup[currentIEID] = false

    }
    /*IE回调函数*/
    //TODO 处理Timeout
    IECallback(dataType, frameId) {
        /*回复空闲状态*/
        IEFormGroup[frameId] = true

        const frame = document.getElementById(`ajax_upload_file_frame_${this.IETag}${frameId}`)
        const resp = {}
        const content = frame.contentWindow ? frame.contentWindow.document.body : frame.contentDocument.document.body
        if(!content) throw new Error('Your browser does not support async upload')
        try {
            resp.responseText = content.innerHTML || 'null innerHTML'
            resp.json = JSON ? JSON.parse(resp.responseText) : eval(`(${resp.responseText})`)
        } catch (e) {
            /*如果是包含了<pre>*/
            if (e.message && e.message.indexOf('Unexpected token') >= 0) {
                /*包含返回的json*/
                if (resp.responseText.indexOf('{') >= 0) {
                    const msg = resp.responseText.substring(resp.responseText.indexOf('{'), resp.responseText.lastIndexOf('}') + 1)
                    return JSON ? JSON.parse(msg) : eval(`(${msg})`)
                }
                return {type: 'FINISHERROR', message: e.message}
            }
            throw e
        }
        return dataType == 'json' ? resp.json : resp.responseText
    }

    /*外部调用方法，主动触发选择文件（等同于调用btn.click()), 仅支持现代浏览器*/
    forwardChoose() {
        if(this.isIE) return false
        this.commonChooseFile()
    }

    /**
     * 外部调用方法，当多文件上传时，用这个方法主动删除列表中某个文件
     * TODO: 此方法应为可以任意操作文件数组
     * @param func 用户调用时传入的函数，函数接收参数files（filesAPI 对象）
     * @return Obj File API 对象
     * File API Obj:
     * {
     *   0 : file,
     *   1 : file,
     *   length : 2
     * }
     */
    fowardRemoveFile(func) {
        this.files = func(this.files)
    }

    /*外部调用方法，传入files（File API）对象可以立刻执行上传动作，IE不支持。调用随后会触发beforeUpload*/
    filesToUpload(files) {
        if(this.isIE) return
        this.files = files
        this.commonUpload()
    }

    /*外部调用方法，取消一个正在进行的xhr，传入id指定xhr（doupload时返回）或者默认取消最近一个。*/
    abort(id) {
        id === undefined ? 
            xhrList[currentXHRID].abort() :
            xhrList[id].abort()
    }

    /*判断ie版本*/
    checkIE() {
        const userAgent = this.userAgent;
        const version = userAgent.indexOf('MSIE')
        if (version < 0) return -1

        return parseFloat(userAgent.substring(version + 5, userAgent.indexOf(';', version)))
    }

    /*生成假的IE上传进度*/
    fakeProgress() {
        let add = 6
        const decrease = 0.3,
          end = 98,
          min = 0.2
        return (lastTime) => {
            let start = lastTime
            if (start >= end) return start

            start += add
            add = add - decrease
            add < min && (add = min)

            return start
        }
    }

    getUserAgent() {
        const userAgentString = this.props.options && this.props.options.userAgent;
        const navigatorIsAvailable = typeof navigator !== 'undefined';        
        if (!navigatorIsAvailable && !userAgentString) {
            throw new Error('\`options.userAgent\` must be set rendering react-fileuploader in situations when \`navigator\` is not defined in the global namespace. (on the server, for example)');
        }
        return navigatorIsAvailable ? navigator.userAgent : userAgentString;
    }

    componentWillMount() {
        this.userAgent = this.getUserAgent();
        this.isIE = !(this.checkIE() < 0 || this.checkIE() >= 10)
        /*因为IE每次要用到很多form组，如果在同一页面需要用到多个<FileUpload>可以在options传入tag作为区分。并且不随后续props改变而改变*/
        const tag = this.props.options && this.props.options.tag
        this.IETag = tag ? tag+'_' : ''

        this._updateProps(this.props)
    }

    componentDidMount() {
    }

    componentWillReceiveProps(newProps) {
        this._updateProps(newProps)
    }

    render() {
        return this._packRender()
    }


    /*打包render函数*/
    _packRender() {
        /*IE用iframe表单上传，其他用ajax Formdata*/
        let render = ''
        if (this.isIE) {
            render = this._multiIEForm()
        } else {
            const restAttrs = {
                accept: this.accept,
                multiple: this.multiple
            }

            render = (
                <div className={this.props.className} style={this.props.style}>
                    {this.state.before}
                    <div onClick={(e) => this.commonChooseFile(e)}
                        style={{overflow:'hidden',postion:'relative',display:this.wrapperDisplay}}
                    >
                        {this.state.chooseBtn}
                    </div>
                    {this.state.middle}

                    <div onClick={(e) => this.commonUpload(e)}
                        style={{
                            overflow: 'hidden',
                            postion: 'relative',
                            display: this.chooseAndUpload ? 'none' : this.wrapperDisplay
                        }}
                    >
                        {this.state.uploadBtn}
                    </div>
                    {this.state.after}
                    <input type="file" name="ajax_upload_file_input" ref="ajax_upload_file_input"
                        style={{display:'none'}} onChange={(e) => this.commonChange(e)}
                        {...restAttrs}
                    />
                </div>
            )
        }
        return render
    }

    /*IE多文件同时上传，需要多个表单+多个form组合。根据currentIEID代表有多少个form。*/
    /*所有不在空闲（正在上传）的上传组都以display:none的形式插入，第一个空闲的上传组会display:block捕捉。*/
    _multiIEForm() {
        const formArr = []
        let hasFree = false

        /* IE情况下，由于上传按钮被隐藏的input覆盖，不能进行disabled按钮处理。
         * 所以当disabledIEChoose为true（或者func返回值为true）时，禁止IE上传。
         */
        const isDisabled =
          typeof this.disabledIEChoose === 'function' ? this.disabledIEChoose() : this.disabledIEChoose

        /*这里IEFormGroup的长度会变，所以不能存len*/
        for(let i = 0; i<IEFormGroup.length;  i++) {
            _insertIEForm.call(this,formArr,i)
            /*如果当前上传组是空闲，hasFree=true，并且指定当前上传组ID*/
            if(IEFormGroup[i] && !hasFree) {
                hasFree = true
                currentIEID = i
            }
            /*如果所有上传组都不是空闲状态，push一个新增组*/
            (i==IEFormGroup.length-1) && !hasFree && IEFormGroup.push(true)

        }

        return (
            <div className={this.props.className} style={this.props.style} id="react-file-uploader">
                {formArr}
            </div>
        )

        function _insertIEForm(formArr,i) {
            /*如果已经push了空闲组而当前也是空闲组*/
            if(IEFormGroup[i] && hasFree) return
            /*是否display*/
            const isShow = IEFormGroup[i]
            /*Input内联样式*/
            const style = {
                position:'absolute',
                left:'-30px',
                top:0,
                zIndex:'50',
                fontSize:'80px',
                width:'200px',
                opacity:0,
                filter:'alpha(opacity=0)'
            }

            /*是否限制了文件后缀，以及是否disabled*/
            const restAttrs = {
                accept: this.accept,
                disabled: isDisabled
            }

            const input =
                <input type="file" name={`ajax_upload_hidden_input_${i}`} id={`ajax_upload_hidden_input_${i}`}
                    ref={`ajax_upload_hidden_input_${i}`} onChange={(e) => this.IEChooseFile(e)} onClick={(e) => this.IEBeforeChoose(e)}
                    style={style} {...restAttrs}
                />

            i = `${this.IETag}${i}`
            formArr.push((
                <form id={`ajax_upload_file_form_${i}`} method="post" target={`ajax_upload_file_frame_${i}`}
                    key={`ajax_upload_file_form_${i}`}
                    encType="multipart/form-data" ref={`form_${i}`} onSubmit={(e) => this.IEUpload(e)}
                    style={{display:isShow? 'block':'none'}}
                >
                    {this.state.before}
                    <div style={{overflow:'hidden',position:'relative',display:'inline-block'}}>
                        {this.state.chooseBtn}
                        {/*input file 的name不能省略*/}
                        {input}
                    </div>
                    {this.state.middle}
                    <div style={{
                        overflow:'hidden',
                        position:'relative',
                        display:this.chooseAndUpload?'none':this.wrapperDisplay
                        }}
                    >
                        {this.state.uploadBtn}
                        <input type="submit"
                            style={{
                                position:'absolute',
                                left:0,
                                top:0,
                                fontSize:'50px',
                                width:'200px',
                                opacity:0
                            }}
                        />
                    </div>
                    {this.state.after}
                </form>
            ))
            formArr.push((
                <iframe id={`ajax_upload_file_frame_${i}`}
                    name={`ajax_upload_file_frame_${i}`}
                    key={`ajax_upload_file_frame_${i}`}
                    className="ajax_upload_file_frame"
                    style={{
                        display: 'none',
                        width: 0,
                        height: 0,
                        margin: 0,
                        border: 0
                    }}
                >
                </iframe>
            ))
        }
    }
}

FileUpload.propTypes = {
    options: PT.shape({
        /*basics*/
        baseUrl: PT.string.isRequired,
        param: PT.oneOfType([PT.object, PT.func]),
        dataType: PT.string,
        chooseAndUpload: PT.bool,
        paramAddToField: PT.oneOfType([PT.object, PT.func]),
        wrapperDisplay: PT.string,
        timeout: PT.number,
        accept: PT.string,
        multiple: PT.bool,
        numberLimit: PT.oneOfType([PT.number, PT.func]),
        fileFieldName: PT.oneOfType([PT.string, PT.func]),
        withCredentials: PT.bool,
        requestHeaders: PT.object,
        /*specials*/
        tag: PT.string,
        userAgent: PT.string,
        disabledIEChoose: PT.oneOfType([PT.bool, PT.func]),
        _withoutFileUpload: PT.bool,
        filesToUpload: PT.arrayOf(PT.object),
        textBeforeFiles: PT.bool,
        /*funcs*/
        beforeChoose: PT.func,
        chooseFile: PT.func,
        beforeUpload: PT.func,
        doUpload: PT.func,
        uploading: PT.func,
        uploadSuccess: PT.func,
        uploadError: PT.func,
        uploadFail: PT.func,
        onabort: PT.func
    }).isRequired,
    style: PT.object,
    className: PT.string
}

export default FileUpload